#include <errno.h>

#include "gtconst.h"
#include "gtthread.h"

extern p_gtthread p_cur_thread;

int atomic_exchange(gtthread_mutex_t *mutex, int value)
{
    int ret = 1;
    __asm__ __volatile__(
        "movl %2, %%eax\n\t"
        "xchg %%eax, %0\n\t"
        "movl %%eax, %1\n\t"
        :"=m"(mutex->lock), "=r"(ret)
        :"a"(value), "m"(mutex->lock)
        :
    );
    return ret;
}


int gtthread_mutex_init(gtthread_mutex_t* mutex)
{
    GT_DEBUG_OUT("[gtthread_mutex_init] initializing mutex");
    if (mutex == NULL)
        return EINVAL;
    mutex->initialized = 1;
    mutex->lock = 0;
    mutex->owner = -1;
    return 0;
}

int gtthread_mutex_lock(gtthread_mutex_t* mutex)
{
    GT_DEBUG_OUT1("[gtthread_mutex_lock] thread %d is trying to lock mutex", (int)p_cur_thread->tid);
    gtthread_t self = gtthread_self();
    if (self == mutex->owner) {
        GT_DEBUG_OUT1("[gtthread_mutex_lock] ===== deadlock %d", self);
        return EDEADLK; // already owned mutex
    }
    if (!mutex->initialized || (mutex->lock > 1 || mutex->lock < 0)) {
        GT_DEBUG_OUT1("[gtthread_mutex_lock] ====== no init %d", self);
        return EINVAL; // not initialized mutex
    }
    while (atomic_exchange(mutex, 1) != 0) {
        GT_DEBUG_OUT1("[gtthread_mutex_lock] thread %d locking mutex failed, now yielding", (int)p_cur_thread->tid);
        GT_DEBUG_OUT1("[gtthread_mutex_lock]mutexvalue %d", mutex->lock);
        gtthread_yield();
    }
    GT_DEBUG_OUT1("[gtthread_mutex_lock] thread %d successfully locked mutex", (int)p_cur_thread->tid);
    mutex->owner = self;
    GT_DEBUG_OUT1("[gtthread_mutex_lock] owner %d", self);
    return 0;
}

int gtthread_mutex_unlock(gtthread_mutex_t* mutex)
{
    if (mutex->owner != gtthread_self()) {
        GT_DEBUG_OUT1("[gtthread_mutex_unlock] ============== not owner %d", gtthread_self());
        return EPERM; // not owner
    }
    mutex->lock = 0;
    GT_DEBUG_OUT1("[gtthread_mutex_unlock] thread %d unlocked mutex", (int)p_cur_thread->tid);
    return 0;
}




